/**
 * @file proto_plat.h
 * @author ZMC (czm@comtika.com)
 * @brief commit IOT protocol v3.0
 * @version 1.0.0.0
 * @date 2024-07-05
 * 
 * 
 */


////    #include "proto_plat.h"


#ifndef __PROTO_PLAT_T
#define __PROTO_PLAT_T

#include "pb.h"
#include <stdint.h>

#define TLV_VERSION (0x03)         //当前协议版本号
#define TLV_HEAD    (0xAA)         //帧起始符： 用Tag表示，标识一帧数据的开始，其值固定为AAH
#define TLV_MAX_LEN (1280 + 128)   //报文体长度： 用BL表示，指报文体的字节数，最大长度1280字节

#define TLV_TXBUF_SIZE (1024 + 32)
#define TLV_TXPB_SIZE (TLV_TXBUF_SIZE - sizeof(tlv_head_t)-sizeof(tlv_t)-2)



//#define  TLV_ReportPeriod_sec   ( 10*60 )   // 节省流量起见，最小上报周期为10min
//#define  TLV_ReportPeriod_sec   ( 3*60+23 )   // 测试阶段最小上报周期为2min report-------这需要大于等于-iot_Node_Test_tick_min( )轮询周期---否则意义不大  
#define  TLV_ReportPeriod_sec   (  37 )   // 测试阶段最小上报周期为15sec
//#define  TLV_ReportPeriod_sec   ( 1 )

//#define  TLV_PollPeriod_sec   ( 20 )   //轮询节点的周期 一般需要小于主动上报周期。即 TLV_PollPeriod_sec < TLV_ReportPeriod_sec
#define  TLV_PollPeriod_sec   ( 3 )   //轮询节点的周期 一般需要小于主动上报周期。即 TLV_PollPeriod_sec < TLV_ReportPeriod_sec

#define  TLV_heartbeat_retry_max   ( 2u )   ///心跳最大重发次数，超过这个次数则重发登录包

#define  TLV_HeartbeatPeriod_ms   ( 58000u )  //心跳周期
//#define  TLV_HeartbeatPeriod_ms   ( 37000u )  //心跳周期


//#define  TLV_Write_wait_us   ( 500000 )  //// ////经测试500ms刚好合适，100ms太快，1sec太慢
#define  TLV_Write_wait_us   ( 970000 )  //// ////经测试970ms刚好合适，100ms太快，1sec太慢

#define  TLV_Write_one_wait_us   ( 470000 )  ////写单个寄存器的响应时间


#define  TLV_Write_iot_id_num_max   ( 4u ) ////一次写iot_id 个数的最大值



/*---------------------------------------------
typedef enum{
    network_init,
    network_ready,
    network_wait_connect,
    network_connected,
}network_state_e;


typedef struct{
    network_state_e network_state;


    //modular info
    char SN[32+1];
    char IMEI[15+1];
    char IMSI[15+1];
    char CSQ;
    char BER;
    char CCID[20+1];
    char COPS[32+1];
    //socket info
    int8_t socket;

#if 0    
    //buf
    uint8_t rxbuf[1600];
    uint16_t rxlen;
    uint8_t tx_idle;
#endif    

}cat1_handle_t;

extern cat1_handle_t hcat1;
extern char connect_err[64];

-------------------------------------------*/


#define  PROTO_PLAT_PRINT_ENABLE       (0x01)    //本文件调试打印使能



// 6.3全局响应码
// 序号	响应码(ACK)	说明
// 1	01	集中器成功受理
// 2	02	命令或数据格式错误
// 3	03	集中器忙
// 4	04	终端原因不受控（终端是指网关子设备）
// 5	05	当前设备模式不支持远程控制
// 6	06	子设备ID不存在
// 7	07~15	预留（平台定义）
// 8	16~255	其它错误码（设备自定义，共240个）

typedef enum{
    tlv_ack_no = 0x00,
    tlv_ack_ok = 0x01,
    tlv_ack_format_error = 0x02,    //命令或数据格式错误
    tlv_ack_busy = 0x03,            //设备忙
    tlv_ack_subdev_error = 0x04,    //子设备错误（网关才有）
    tlv_ack_mode_error = 0x05,      //当前设备模式不支持远程控制（网关才有）
    tlv_ack_invalid_id = 0x06,      //子设备id不存在（网关才有）
    tlv_ack_invalid = 0x30,         //不支持的功能  48---
    tlv_ack_pb_decode_error = 0x31,   //-pb解码失败 49----
}tlv_ack_e;




/////北向协议命令码
typedef enum{

    tlv_cmd_node_read_attr  =  0x65,  //读子设备属性,
    tlv_cmd_node_write_attr =  0x66,  //写子设备属性,


    PB_CMD_OTA_get_space      = 0xC0,//GetOtaDiskSpaceUp 

    PB_CMD_OTA_PACKET_START   = 0xC1, //packet 平台通知设备即将开始固件传输 C1H
    PB_CMD_OTA_PACKET_SEND ,
    PB_CMD_OTA_PACKET_STOP ,
    PB_CMD_OTA_PACKET_PROCESS ,

    PB_CMD_OTA_HTTP_START     = 0xC6, // 平台通知设备启动OTA升级 C6H 
    PB_CMD_OTA_HTTP_PROCESS   = 0xC7, // 网关上报进度

}tlv_pb_cmd_e;




////7.5.1查询设备支持及固件存储空间 C0H
// support	设备支持OTA升级方式	
enum {
	OTA_C0_MODE_PACKET = 1,          // 1：仅支持平台分帧推送方式
	OTA_C0_MODE_HTTP,                // 2：仅支持设备HTTP下载方式
	OTA_C0_MODE_PACKET_AND_HTTP,     // 3：同时支持以上两种方式
};

#define OTA_C0_MODE_SELECT   (OTA_C0_MODE_HTTP)  //// 当前选择的OTA升级方式	





////7.5.3.1平台通知设备启动OTA升级 C6H
// firmware_type	升级固件类型	
// 1：Host； 2：Unit； 
// 3：PLC-Master； 4：PLC-Slave
 typedef enum {
	OTA_C1_6_FW_TYPE_HOST = 1,
	OTA_C1_6_FW_TYPE_UNIT,
	OTA_C1_6_FW_TYPE_PLC_MASTER,
	OTA_C1_6_FW_TYPE_PLC_SLAVE

} OTA_firmware_type_e ;





typedef struct{
    uint8_t  login;
    uint8_t  login_cnt;
    uint16_t heartbeat_cnt;
    uint32_t last_conn_time;
}plat_comm_t;

extern plat_comm_t hplat_comm;


////---dan---
typedef enum{
    tlv_head2_net_CCT  = 0x00,    //0=网关集中器联网
    tlv_head2_net_NODE = 0x01,    //1=设备直接联网  

}tlv_head2_net_e;



// 1	c_type	控制类型	见枚举定义 1：单点控制，2：分组控制，3：广播控制
typedef enum{
    tlv_ctr_point = 0x01,    //1：单点控制
    tlv_ctr_group,           //2：分组控制
    tlv_ctr_broadcast,       //3：广播控制

} tlv_ctr_type_e;



#pragma pack(1)

typedef struct{
    uint8_t dir:1;  //通信方向1=server->client, 0=client->server
    uint8_t AES:1;  //1=AES加密, 0=不加密
    uint8_t net:1;  //1=设备直接联网
    uint8_t unsupported:1;//1=不支持该指令
    uint8_t res:4;
}tlv_flag_t;

typedef struct{
    uint8_t version; //协议版本
    uint8_t seq;     //序列号，收发保持一致
    tlv_flag_t flag; //标志位
    uint8_t cmd;     //命令码   
    uint8_t ext[];   //头部扩展字段
}tlv_head_t, *ptlv_head_t;

typedef struct{
    uint8_t head;
    uint8_t head_len;
    uint8_t body_lenL;
    uint8_t body_lenH;
    uint8_t data[];
}tlv_t, *ptlv_t;


typedef struct{
    //ota info
    uint32_t firmware_size;
    uint32_t firmware_crc32;
    uint8_t firmware_type;
    uint8_t node_id[6];
    uint16_t unit_type;
    uint8_t radio;
    uint8_t firmware_version[32];

    //ota control
    uint16_t total;
    uint16_t expect;

    uint32_t flash_addr;

}ota_handle_t;



#pragma pack()





#pragma pack(1)


typedef union {
   
    uint8_t  u8[4];  
    uint32_t u32;

}   little_u32_u;//Little-Endian



#pragma pack()






/**
 * @brief 主动发送数据给平台时获取seq ?????
 * 
 * @return uint8_t 
 */
uint8_t tlv_get_seq(void);



/**
 * @brief 
 * 
 * @param data 
 * @param len 
 * @return ptlv NULL=failed， other，pointer of tlv frame
 */
ptlv_t tlv_check(uint8_t* data, uint16_t len);


/**
 * @brief body部分已经写好，调用本函数填写头部， 增加校验码[避免复制来复制去]
 * 
 * @param buf 
 * @param cmd 
 * @param seq 
 * @param body_len 
 * @return uint16_t 最终帧的长度
 */
uint16_t tlv_seal(ptlv_t buf, uint8_t cmd, uint8_t seq, uint16_t body_len);



/**
 * @brief 打包一个tlv-pb包，注意和tlv_seal的区别
 * 
 * @param buf 
 * @param cmd 
 * @param seq 
 * @param body_len 
 * @return uint16_t 最终帧的长度
 */
uint16_t tlv_pack(ptlv_t buf, uint8_t cmd, uint8_t seq, uint8_t*body, uint16_t body_len);


/**
 * @brief 不支持的指令
 * 
 * @param buf 
 * @param cmd 
 * @return uint16_t 
 */
uint16_t tlv_unsupported(ptlv_t buf, uint8_t cmd);



/*********************************************************************************** */


uint16_t pb_ack_encode(pb_ostream_t* postream, uint8_t ack);


/**
 * @brief 登陆帧封包
 * 
 * @param buf 
 * @return uint16_t 登陆帧长度
 */
uint16_t tlv_login(uint8_t* buf);



/**
 * @brief 心跳包封包
 * 
 * @param buf 
 * @return uint16_t 
 */
uint16_t tlv_heartbeat(uint8_t* buf);



/**
 * @brief 7.3.2.5拉取网关子设备拓扑 64H
 * // 	命 令 码：64H
 * // 	功    能：仅当集中器升级后，若集中器本身无任何子设备时，可向平台主动拉取子设备拓扑
 * // 	方    向：D -> S
 * //	数 据 域：无 
 * //	响 应 帧 PROTO结构如下：
 * // 	message SendSubDeviceDown {}，详见7.3.2.1
 * // 	说明：
 * // 该命令实现后，可以做到升级无缝衔接。
 * // ??????集中器收到数据是否需要应答??????   
 * 
 * @param buf 
 * @return uint16_t 
 */
uint16_t tlv_get_topo_from_server(uint8_t* buf);// 该命令实现后，可以做到升级无缝衔接。


//设备主动上报OTA升级进度 C7H
uint16_t pb_ota_http_process_report(uint8_t* buf,uint8_t err_code, uint8_t stage, uint8_t percent);



/**
 * @brief 93 主动上报定时任务执行任务结果
 * 
 * @param pf_tx 
 * @param task_num 
 * @param error 0=ok,其它=error code 
 */
uint16_t pb_report_time_task(ptlv_t pf_tx, uint8_t task_num, uint8_t error);


/**
 * @brief 北向tlv-pb协议接收处理，src为消息来源,暂不支持重入
 * 
 * @param rxbuf
 * @param rxlen 
 * @param src 
 */
 ///////------上行协议的核心处理句柄----------------------
void tlv_recv_handler(uint8_t*rx_flag, uint8_t*rxbuf, uint16_t rxlen, uint8_t src);

/**
 * @brief 登录，心跳等
 * 
 */
void plat_conn_manager(void);


/**
 * @brief 主动上报设备属性
 * 
 */
void plat_report_attr_process(void);


void plat_report_attr_tick_sec(void);

void proto_pb_global_ack( uint8_t cmd,uint8_t seq,tlv_ack_e ack_code);



#endif








